--[[
    ElementLayout: 实现元素布局样式功能
]]

local ElementXml = _ENV.import("./ElementXml.lua");
local ElementLayout = commonlib.inherit(ElementXml);

ElementLayout:Property("NoneElement", false, "IsNoneElement");                          -- 是否空元素
ElementLayout:Property("BlockElement", false, "IsBlockElement");                        -- 是否块元素
ElementLayout:Property("InlineElement", false, "IsInlineElement");                      -- 是否内联元素
ElementLayout:Property("FlexElement", false, "IsFlexElement");                          -- 是否弹性元素
ElementLayout:Property("PositionElement", false, "IsPositionElement");                  -- 是否定位元素
ElementLayout:Property("AbsoluteElement", false, "IsAbsoluteElement");                  -- 是否是绝对定位元素
ElementLayout:Property("FixedElement", false, "IsFixedElement");                        -- 是否是固定定位元素

function ElementLayout:ctor()
    self:SetClassName("ElementLayout");
    -- 视图区域 outline + border + padding + content
    -- 相对父元素的坐标
    self.m_x, self.m_y, self.m_width, self.m_height = 0, 0, 0, 0;
    -- 相对窗口的坐标
    self.m_window_x, self.m_window_y = 0, 0;
end

-- 初始化函数
function ElementLayout:Init(xmlnode, parent)
    ElementLayout._super.Init(self, xmlnode, parent);
    return self;
end

-- 获取窗口
function ElementLayout:GetWindow()
    local parent = self:GetParent();
    return parent and parent:GetWindow() or nil;
end

-- 设置元素窗口位置
function ElementLayout:SetWindowXY(x, y)
    self.m_window_x, self.m_window_y = x or self.m_window_x or 0, y or self.m_window_y or 0;
end

-- 获取元素窗口位置
function ElementLayout:GetWindowXY()
    return self.m_window_x or 0, self.m_window_y or 0;
end

-- 获取局部X坐标
function ElementLayout:GetX()
    return self.m_x or 0;
end

-- 获取局部Y坐标
function ElementLayout:GetY()
    return self.m_y or 0;
end

-- 获取局部X Y坐标
function ElementLayout:GetXY()
    return self.m_x or 0, self.m_y or 0;
end

-- 设置局部X Y坐标
function ElementLayout:SetXY(x, y)
    self.m_x , self.m_y = x or self.m_x or 0, y or self.m_y or 0;
end

-- 获取元素宽
function ElementLayout:GetWidth()
    return self.m_width;
end

-- 获取元素高
function ElementLayout:GetHeight()
    return self.m_height;
end

-- 获取局部宽高
function ElementLayout:GetWidthHeight()
    return self.m_width or 0, self.m_height or 0;
end

-- 设置局部宽高
function ElementLayout:SetWidthHeight(width, height)
    self.m_width, self.m_height = width or self.width or 0, height or self.m_height or 0;
end

-- 获取文本宽高
function ElementLayout:GetTextWidthHeight(text, font_size)
    return commonlib.GetTextWidthHeight(text, font_size or self.m_font_size or 16, "__default__");
end

-- 获取指定宽度文本
function ElementLayout:GetTextByWidth(text, max_width, font_size)
    if(not text or text== "") then return "", "" end
    if (not max_width) then return text, "" end

    local width = self:GetTextWidthHeight(text, font_size);
    if(width <= max_width) then return text, "" end

    local nSize = commonlib.utf8_len(text);
    local iStart, iEnd, iMid = 1, nSize, nSize;

    while (iStart <= iEnd) do
        width = self:GetTextWidthHeight(commonlib.utf8_sub(text, 1, iMid), font_size);
        if(width > max_width) then
            iEnd = iMid - 1;
        else
            iStart = iMid + 1;
        end
        iMid = math.floor((iStart + iEnd) / 2);
    end

    return commonlib.utf8_sub(text, 1, iMid), commonlib.utf8_sub(text, iMid + 1);
end

-- 获取元素局部位置
function ElementLayout:GetPosition()
    return self.m_x or 0, self.m_y or 0, self.m_width or 0, self.m_height or 0;
end

-- 获取边框宽度 left top right bottom
function ElementLayout:GetBorderWidth()
    return self.m_border_left_width or 0, self.m_border_top_width or 0, self.m_border_right_width or 0, self.m_border_bottom_width or 0;
end

-- 设置填充 left top right bottom 
function ElementLayout:SetPadding(left, top, right, bottom)
    self.m_padding_left, self.m_padding_top, self.m_padding_right, self.m_padding_bottom = left or 0, top or 0, right or 0, bottom or 0;
end

-- 获取填充 left top right bottom
function ElementLayout:GetPadding()
	return self.m_padding_left or 0, self.m_padding_top or 0, self.m_padding_right or 0, self.m_padding_bottom or 0;
end

-- 获取内容XY坐标
function ElementLayout:GetContentXY()
    local x, y = self:GetXY();
    local padding_left, padding_top = self:GetPadding();
    local border_left_width, border_top_width = self:GetBorderWidth();
    return x + border_left_width + padding_left, y + border_top_width + padding_top;
end

-- 获取最大内容宽高
function ElementLayout:GetMaxContentWidthHeight()
    local width, height = self:GetWidthHeight();
    local padding_left, padding_top, padding_right, padding_bottom = self:GetPadding();
    local border_left_width, border_top_width, border_right_width, border_bottom_width = self:GetBorderWidth();
    return width - border_left_width - padding_left - border_right_width - padding_right, height - border_top_width - padding_top - border_bottom_width - padding_bottom;
end

-- 获取内容宽高
function ElementLayout:GetContentWidthHeight()
    return self.m_content_width or 0, self.m_content_height or 0;
end

-- 设置内容宽高
function ElementLayout:SetContentWidthHeight(width, height)
    self.m_content_width, self.m_content_height = width or self.m_content_width or 0, height or self.m_content_height or 0;
end

-- 布局更新前回调
function ElementLayout:OnBeforeUpdateLayout()
    local style = self:GetStyle();
    local parent = self:GetParent();
    local parent_local_width, parent_local_height;
    if (parent == nil) then
        parent_local_width, parent_local_height = self:GetWindow():GetWindowWidthHeight();
    else
        parent_local_width, parent_local_height = parent:GetWidthHeight();
    end
    -- 提取生效样式后即可确定的样式值, 外观相关
    self.m_display = style:GetStyleValue("display", "block");
    -- 默认为父元素宽高
    self.m_width = style:GetPixelStyleValue("width", parent_local_width, parent_local_width);
    self.m_height = style:GetPixelStyleValue("height", parent_local_height, parent_local_height);
    self.m_padding_top = style:GetPixelStyleValue("padding-top", 0, parent_local_width);
    self.m_padding_right = style:GetPixelStyleValue("padding-right", 0, parent_local_width);
    self.m_padding_bottom = style:GetPixelStyleValue("padding-bottom", 0, parent_local_width);
    self.m_padding_left = style:GetPixelStyleValue("padding-left", 0, parent_local_width);
    self.m_margin_top = style:GetPixelStyleValue("margin-top", 0, parent_local_width);
    self.m_margin_right = style:GetPixelStyleValue("margin-right", 0, parent_local_width);
    self.m_margin_bottom = style:GetPixelStyleValue("margin-bottom", 0, parent_local_width);
    self.m_margin_left = style:GetPixelStyleValue("margin-left", 0, parent_local_width);
    self.m_margin_top = style:GetPixelStyleValue("margin-top", 0, parent_local_width);
    self.m_margin_right = style:GetPixelStyleValue("margin-right", 0, parent_local_width);
    self.m_margin_bottom = style:GetPixelStyleValue("margin-bottom", 0, parent_local_width);
    self.m_margin_left = style:GetPixelStyleValue("margin-left", 0, parent_local_width);
    self.m_border_top_width = style:GetPixelStyleValue("border-top-width", 0, parent_local_width);
    self.m_border_right_width = style:GetPixelStyleValue("border-right-width", 0, parent_local_width);
    self.m_border_bottom_width = style:GetPixelStyleValue("border-bottom-width", 0, parent_local_width);
    self.m_border_left_width = style:GetPixelStyleValue("border-left-width", 0, parent_local_width);
    self.m_border_top_color = style:GetStyleValue("border-top-color", 0);
    self.m_border_right_color = style:GetStyleValue("border-right-color", 0);
    self.m_border_bottom_color = style:GetStyleValue("border-bottom-color", 0);
    self.m_border_left_color = style:GetStyleValue("border-left-color", 0);
    self.m_background_color = style:GetStyleValue("background-color", 0);
    self.m_color = style:GetStyleValue("color");
    self.m_font_size = style:GetStyleValue("font-size");
    self.m_line_height = style:GetStyleValue("line-height");
    self.m_float = style:GetStyleValue("float", "none");
    self.m_position = style:GetStyleValue("position", "static");
    self.m_top = style:GetPixelStyleValue("top", 0, parent_local_width);
    self.m_right = style:GetPixelStyleValue("right", 0, parent_local_width);
    self.m_bottom = style:GetPixelStyleValue("bottom", 0, parent_local_width);
    self.m_left = style:GetPixelStyleValue("left", 0, parent_local_width);
end

-- 开始布局
function ElementLayout:OnBeginLayout()
    self:SetFlexElement(self.m_display == "flex" or self.m_display == "inline-flex");
    self:SetInlineElement(self.m_display == "inline" or self.m_display == "inline-block" or self.m_display == "inline-flex");
    self:SetBlockElement(not self:IsInlineElement());
    self:SetNoneElement(self.m_display == "none");
    self:SetAbsoluteElement(self.m_position == "absoulte");
    self:SetFixedElement(self.m_position == "fixed");
    self:SetPositionElement(self:IsAbsoluteElement() or self:IsFixedElement());
    return self;
end

-- 子元素布局更新前回调
function ElementLayout:OnBeforeUpdateChildrenLayout()
    return self;
end

-- 更新子元素布局
function ElementLayout:OnUpdateChildrenLayout()
    for children in self:ChildrenIterator() do
        children:UpdateLayout();
    end
end

-- 子元素布局更新后回调
function ElementLayout:OnAfterUpdateChildrenLayout()
    return self;
end

-- 更新元素布局
function ElementLayout:OnUpdateLayout()
    self:OnBoxUpdateLayout();
end

-- 结束布局
function ElementLayout:OnEndLayout()
    return self;
end

-- 元素布局更新后回调
function ElementLayout:OnAfterUpdateLayout()
    return self;
end

-- 更新布局
function ElementLayout:UpdateLayout()
    -- 布局更新前回调
    self:OnBeforeUpdateLayout();

    -- 开始布局
    self:OnBeginLayout();

    -- 子元素布局更新前回调
    self:OnBeforeUpdateChildrenLayout();

    -- 更新子元素布局
    self:OnUpdateChildrenLayout();

    -- 执行子元素布局后回调
    self:OnAfterUpdateChildrenLayout();

    -- 更新元素布局
    self:OnUpdateLayout();

    -- 结束布局
    self:OnEndLayout();

    -- 元素布局更新后回调
    self:OnAfterUpdateLayout();
end

function ElementLayout:OnBoxUpdateLayout()
    if (not self:IsBlockElement() or self:IsNoneElement()) then return end
    local available_x, right_available_x, content_width, content_height = 0, 0, 0, 0;
    local max_content_width = self:GetMaxContentWidthHeight();
    local line_height = 0;
    for children in self:ChildrenIterator() do
        local children_width = children.m_width + children.m_margin_left + children.m_margin_right;
        local children_height = children.m_height + children.m_margin_top + children.m_margin_bottom;
        local is_right_float = children.m_float == "right";
        if (not children:IsNoneElement() and not children:IsPositionElement()) then
            local children_left, children_top;
            if (children:IsInlineElement()) then
                -- 空间不够 新起一行
                if ((max_content_width - available_x - right_available_x) < children_width) then
                    available_x = 0; right_available_x = 0;
                    content_height = content_height + line_height;
                    line_height = 0;
                end
                if (is_right_float) then
                    right_available_x = right_available_x + children_width;
                    children_left = max_content_width - right_available_x;
                    children_top = content_height + children.m_margin_top;
                else
                    children_left = available_x + children.m_margin_left;
                    available_x = available_x + children_width;
                    children_top = content_height + children.m_margin_top;
                end
                line_height = commonlib.if_else(line_height > children_height, line_height, children_height);
                content_width = commonlib.if_else(content_width > (available_x + right_available_x), content_width, available_x + right_available_x);
            else
                -- 块元素独占一行
                available_x = 0; right_available_x = 0;
                content_height = content_height + line_height;               -- 上一行内联元素的高度
                line_height = 0;

                children_left = children.m_margin_left;
                children_top = content_height + children.m_margin_top;

                content_height = content_height + children_height;           -- 当前行的高度
                content_width = commonlib.if_else(content_width > children_width, content_width, children_width);
            end
            children:SetXY(children_left or 0, children_top or 0);
        end
    end
    content_height = content_height + line_height;               -- 上一行内联元素的高度
    self:SetContentWidthHeight(content_width, content_height);
end

-- 获取背景色
function ElementLayout:GetBackgroundColor()
    return self.m_background_color or 0;
end

function ElementLayout:Dump()
    print("=======================Element Dump Begin======================");
    print("ClassName:", self:GetClassName());
    print("TagName:", self:GetTagName());
    print("x, y:", self:GetXY());
    print("width, height:", self:GetWidthHeight());
    print("=======================Element Dump End======================");

    for children in self:ChildrenIterator() do
        children:Dump();
    end
end

return ElementLayout;
